#region MIT License
//Copyright (c) 2007 Shane Isbell
//
// Permission is hereby granted, free of charge, to any person obtaining a copy 
// of this software and associated documentation files (the "Software"), to 
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
// THE SOFTWARE. 
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;

using Archivist.Tar;

namespace Archivist.Tar.ConsoleRunner
{
    class Program
    {
        static int Main(string[] args)
        {
            try
            {
                //List(l), Extract(x)
                if (args.Length == 0 || !args[0].StartsWith("-"))
                {
                    Console.Error.WriteLine("No options specified");
                    return 1;
                }

                ICollection<Option> options = GetOptionsFrom(args);
                if (!IsValid(options))
                {
                    return 1;
                }

                if (options.Contains(Option.List))
                {
                    TarFile tarStream = new TarFile(new FileStream(@args[1], FileMode.Open));
                    ICollection<TarEntry> es = tarStream.GetTarEntries();

                    if (options.Contains(Option.Verbose))
                    {
                        foreach (TarEntry e in es)
                        {
                            Console.WriteLine("{0}/{1} {2} {3} {4}", e.UserId, e.GroupId,
                                e.ModificationTime.ToShortDateString(), e.Length, e.FileName.Trim('\0'));
                        }
                    }
                    else
                    {
                        foreach (TarEntry e in es)
                        {
                            Console.WriteLine(e.FileName.Trim('\0'));
                        }
                    }
                    Console.WriteLine("Tar Entry Count: {0}", es.Count);
                    return 0;
                }
                else if (options.Contains(Option.Extract))
                {
                    Stream stream = null;
                    if (options.Contains(Option.GZip))
                    {
                      //  stream = new MemoryStream();
                        FileStream fileStream = new FileStream(@args[1], FileMode.Open);
                        stream =
                            new GZipStream(fileStream, CompressionMode.Decompress);
                     //   byte[] buffer = new byte[fileStream.Length];
                      //  zipStream.Read(buffer, 0, (int)fileStream.Length);
                     //   stream.Write(buffer, 0, (int)fileStream.Length);
                      //  zipStream.Close();
                    }
                    else
                    {
                        stream = new FileStream(@args[1], FileMode.Open);
                    }

                    TarFile tarStream = new TarFile(stream);
                    ICollection<TarEntry> es = tarStream.GetTarEntries();
                    Console.WriteLine(es.Count);
                    bool verbose = options.Contains(Option.Verbose);
                    foreach (TarEntry e in es)
                    {
                        if (verbose)
                        {
                            Console.WriteLine(e.FileName);
                        }

                        if (e.FileType.Equals(FileType.Directory))
                        {
                            DirectoryInfo directoryInfo =
                                new DirectoryInfo(Environment.CurrentDirectory + @"\" + e.FileName);
                            if (!directoryInfo.Exists)
                            {
                                directoryInfo.Create();
                            }
                        }
                        else
                        {
                            FileInfo fileInfo = new FileInfo(Environment.CurrentDirectory + @"\" + e.FileName);
                            Console.WriteLine(fileInfo.FullName);
                            FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Create);
                            fileStream.Write(e.Data, 0, (int)e.Length);
                            fileStream.Close();
                        }
                    }
                }
                else if (options.Contains(Option.Create))
                {
                    Console.WriteLine("Creating tar file.");
                    ICollection<FileSystemInfo> fsis = new List<FileSystemInfo>();
                    for (int i = 2; i < args.Length; i++)
                    {
                        if (Directory.Exists(args[i]))
                        {
                            fsis.Add(new DirectoryInfo(args[i]));
                        }
                        else if (File.Exists(args[i]))
                        {
                            fsis.Add(new FileInfo(args[i]));
                        }
                        else
                        {
                            throw new ArgumentException("File or directory does not exist: Name = " + args[i]);
                        }
                    }
                    TarFile tarFile = new TarFile(fsis, new DirectoryInfo(Environment.CurrentDirectory));
                    FileStream fileStream = new FileStream(@args[1], FileMode.Create);

                    if (options.Contains(Option.GZip))
                    {
                        GZipStream zipStream = new GZipStream(fileStream, CompressionMode.Compress);
                        MemoryStream memoryStream = new MemoryStream();
                        try
                        {
                            tarFile.WriteTarFileToStream(memoryStream, true);
                            byte[] memoryArray = memoryStream.ToArray();
                            zipStream.Write(memoryArray, 0, memoryArray.Length);
                        }
                        finally
                        {
                            zipStream.Close();
                        }
                    }
                    else
                    {
                        tarFile.WriteTarFileToStream(fileStream);
                    }
                }

                return 0;
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.Message + ":" + e.StackTrace);
                return 1;
            }
        }
        private static ICollection<Option> GetOptionsFrom(String[] args)
        {
            List<Option> options = new List<Option>();
            if (args[0].Contains("l"))
            {
                options.Add(Option.List);
            }
            if (args[0].Contains("x"))
            {
                options.Add(Option.Extract);
            }
            if (args[0].Contains("v"))
            {
                options.Add(Option.Verbose);
            }
            if (args[0].Contains("f"))
            {
                options.Add(Option.File);
            }
            if (args[0].Contains("c"))
            {
                options.Add(Option.Create);
            }
            if (args[0].Contains("z"))
            {
                options.Add(Option.GZip);
            }
            return options;
        }

        private static bool IsValid(ICollection<Option> options)
        {
            if (options.Contains(Option.List) && options.Contains(Option.Extract))
            {
                Console.Error.WriteLine("tar: You may not specify more than one -lx");
                return false;
            }
            else if (options.Count == 0)
            {
                Console.Error.WriteLine("No options specified");
                return false;
            }
            else if ((options.Contains(Option.List) || options.Contains(Option.Extract))
                && !options.Contains(Option.File))
            {
                Console.Error.WriteLine("Must include a file name option.");
                return false;
            }
            return true;
        }
    }
}
